/**
 * WTF Engine
 *
 * License... etc.
 **
 * Render Module
 *
 * Provides:
 *	- Modelview matrix transforms
 *	- render routine
 *  - bindings to important OpenGL calls
 **
 * Authors: Sean Micklethwaite
 **/


#ifndef __renderH__
#define __renderH__

#include "renderable.h"
#include "game.h"
#include "graphics.h"
#include "wtftypes.h"
#include "vector.h"
#include "2d.h"
#include "3d.h"
#include "camera.h"
#include <GL/glext.h>
#include "shader.h"
#include "texture.h"
#include <algorithm>



namespace WtfEngine
{
	/**
	 * The render environment stores variables pertaining to
	class RenderEnvironment {
	public:

	};

	struct RenderState {
		ICamera::tRef Camera;
		IScreen::tRef RenderTarget;
	};
	*/
	
	class IShader: public virtual IGcObject {
	public:
		virtual void Activate() = 0;
		virtual void Deactivate() = 0;

		GC_INTERFACE(IShader);
	};

	/**
	 * The renderer
	 **/
	class Renderer : public Task<Renderer>, public Loggable<Renderer>
	{
	public:	
		typedef ObjectList<IRenderable::tRef>	tRenderList;
		typedef std::vector<IRenderable::tRef>	tOverlayStack;

		typedef enum {
			RENDER_OVERLAYS_ONLY,
			RENDER_WORLD,
			RENDER_WORLD_WIREFRAME,
			RENDER_PHYSICS
		} tRenderType;

		struct GenericPass: public GcObject {
			IScreen::tRef rTarget; //< Render target
			StringPool::Entry sName;

			inline GenericPass(const StringPool::Entry& sName): sName(sName) {};

			virtual void Render() = 0;
			void Activate();
			void Deactivate();

			GC_AUTOSIZE(GenericPass);
		};

		struct PostPass: public GenericPass {
			Texture::tRef rSource; //< Source of diffuse data (results of previous pass)
			IShader::tRef rShader; //< Shader program

			inline PostPass( const StringPool::Entry& sName, IShader::tRefa rShader ): GenericPass(sName), rShader(rShader) {};

			virtual void Render();
			void Activate();
			void Deactivate();

			GC_AUTOSIZE(PostPass);
		};

	private:
		tRenderType				mRenderType;

		/// Static list of objects to be rendered every frame. No visibility
		/// culling is performed on this list.
		tRenderList				mRenderList;

		/// List of objects to be rendered after deferred shading.
		/// Typically useful for special effects e.g. fire, stained glass
		tRenderList				mPostRenderList;
		
		FBOTexture::tRef		mrExposureData;
		FBOTexture::tRef		mrDeferredShaderData;

		ShaderBase::tRef		mpLighting;
		HdrDownsampler::tRef	mpDownsampler;
		ShaderBase::tRef		mpExposureShader;

		tOverlayStack	mOverlayStack;

		ICamera::tRef mrCamera;
		WtfGraphics::tScreenRef mScreen;

		std::vector<GenericPass::tRef> mPipeline;
		FBOTexture::tRef mrRenderTarget;
		FBOTexture::tRef mrNextFreeBuffer;

	public:
		Renderer(WtfGraphics::tScreenRef& screen);
		~Renderer();

		/// Calls render on all renderable objects
		void RenderAll();

		/// Renders the view of the scene from the specified camera, to
		/// the specified screen surface
		void Render(const IScreen::tRef& rScreen, const IPerspective::tRef& rCamera, tRenderType type = RENDER_WORLD);

		inline void PushObject(const IRenderable::tRef& rObj) {
			mRenderList.PushObject(rObj);
		};

		inline void RemoveObject(const IRenderable::tRef& rObj) {
			mRenderList.RemoveObject(rObj);
		};
		
		inline void PushOverlay(const IRenderable::tRef& rOverlay) {
			mOverlayStack.push_back(rOverlay);
		};

		inline void RemoveOverlay(const IRenderable::tRef& rOverlay) {
			tOverlayStack::iterator i;

			for(i = mOverlayStack.begin(); i != mOverlayStack.end(); i++){
				if(*i == rOverlay){
					mOverlayStack.erase(i);
					break;
				};
			};
		};

		void ClearPipeline();
		inline void AppendPass( PostPass::tRefa pass );

		inline bool HasOverlay(const IRenderable::tRef& rOverlay) {
			return (std::find(mOverlayStack.begin(), mOverlayStack.end(), rOverlay) != mOverlayStack.end());
		};
		
		inline void SetCamera(const ICamera::tRef& cam){
			mrCamera = cam;
		};
		inline ICamera* GetCamera(){
			return &*mrCamera;
		};

		IScreen::tRef GetScreen(){
			return mScreen;
		};

		tRenderType getRenderType() const {
			return mRenderType;
		};
		void setRenderType(tRenderType t) {
			mRenderType = t;
		};

		inline FBOTexture::tRef getDeferredShaderData() const {
			return mrDeferredShaderData;
		};
		inline const std::vector<GenericPass::tRef>& getPipeline() const {
			return mPipeline;
		};

	protected:
		void Run();
		void OnPause();
		void OnResume();
		void OnWait();
		void InitScreen();

		GC_AUTOSIZE(Renderer);
	};
};

#endif
